#include "Molas.h"

void ListaMolas::calcular()
{
    #pragma omp single
    {
        for (SPRING* k = modelo->SPRList.Begin; k != NULL;) k = Forca(k);
    }
}
SPRING* ListaMolas::Forca(SPRING*k)
{
    REAL Dist, Dist2, F;
    REAL R[2], V[2];

    k->Translate();

    R[0] = k->R2[0] - k->R1[0];
    R[1] = k->R2[1] - k->R1[1];
    Dist2 = DOT( R, R );
    Dist = sqrt(Dist2);

    V[0] = k->S2->V[0] - k->S1->V[0];
    V[1] = k->S2->V[1] - k->S1->V[1];
    F = ( k->K * (k->L0 * Dist - Dist2) - k->C * DOT( V, R ) ) / Dist2;

    k->S1->Force( -F, R );
    k->S2->Force( F, R );

    k->S1->Torque( -F * (PERP_DOT( R, k->R1 ) - PERP_DOT( R, k->S1->R )) );
    k->S2->Torque( F * (PERP_DOT( R, k->R2 ) - PERP_DOT( R, k->S2->R )) );

    k->MeasureStress( Dist );
    if (k->Stress > k->Mat->Sigma_E) k->L0 += k->L0 * (k->Stress - k->Mat->Sigma_E) / k->Mat->Young;
    else if (-k->Stress > k->Mat->Sigma_E) k->L0 += k->L0 * (k->Stress + k->Mat->Sigma_E) / k->Mat->Young;

    if (Dist > k->FractureT || Dist < k->FractureC)
        return modelo->Delete(k);
    else
        return k->Next;
}

ListaMolas::ListaMolas(MODEL*md)
{
    molas=&md->SPRList;
    modelo=md;
}
ListaMolasParalelo::ListaMolasParalelo(MODEL*md)
{
    molas=&md->SPRList;
    modelo=md;

    for (SPRING* k = modelo->threadsS[0]; k != NULL; k = k->Next) qtdMolas++;
    #if ((defined TESTE_SEMAFORO) || (defined SEMAFORO))
    for (SPHERE* s = modelo->SPHList.Begin; s != NULL;s = s->Next) omp_init_lock(&(s->semaforo));
    #endif
}

void ListaMolasParalelo::calcular()
{
    #pragma omp  for schedule (dynamic,1)
    for (int i = 0; i < modelo->numProceMola; i++)
    {
        SPRING* testeFim;
        if (i!=modelo->numProceMola-1) testeFim = modelo->threadsS[i+1];
        else testeFim=NULL;

        for (SPRING* k = modelo->threadsS[i]; k != testeFim; ) k=Forca(k);
    }
    // barreira implícita
    finalizaOperacoes();
}
SPRING* ListaMolasParalelo::Forca(SPRING*k)
{

    #if (!defined TESTE_SEMAFORO) && (!defined SEMAFORO)

    //#pragma omp critical
    {
        //int tid = omp_get_thread_num();
        //printf("Thread %d ",tid);

        while(k->S1->foiAcessado||k->S2->foiAcessado){printf("df");}
        k->S1->foiAcessado=true;
        k->S2->foiAcessado=true;
        //O flush garante que a variável mantenha coerência na memória principal
        #pragma omp flush
    }
    #endif

    #ifdef TESTE_SEMAFORO
    while (!omp_test_lock(&(k->S1->semaforo)))
    {
        int tid = omp_get_thread_num();
        printf("Thread %d bloqueada na S1. Esfera %d. Mola %d\n",tid,k->S1,k);
    }
    while (!omp_test_lock(&(k->S2->semaforo)))
    {
        int tid = omp_get_thread_num();
        printf("Thread %d bloqueada na S2. Esfera %d. Mola %d\n",tid,k->S2,k);
    }
    #endif

    #if (!(defined TESTE_SEMAFORO) && (defined SEMAFORO))
    omp_set_lock(&(k->S1->semaforo));
    omp_set_lock(&(k->S2->semaforo));
    #endif

    /*REAL Dist, Dist2, F;
    REAL R[2], V[2];
    k->Translate();

    R[0] = k->R2[0] - k->R1[0];
    R[1] = k->R2[1] - k->R1[1];
    Dist2 = DOT( R, R );
    Dist = sqrt(Dist2);

    V[0] = k->S2->V[0] - k->S1->V[0];
    V[1] = k->S2->V[1] - k->S1->V[1];
    F = ( k->K * (k->L0 * Dist - Dist2) - k->C * DOT( V, R ) ) / Dist2;


    k->MeasureStress( Dist );
    if (k->Stress > k->Mat->Sigma_E) k->L0 += k->L0 * (k->Stress - k->Mat->Sigma_E) / k->Mat->Young;
    else if (-k->Stress > k->Mat->Sigma_E) k->L0 += k->L0 * (k->Stress + k->Mat->Sigma_E) / k->Mat->Young;

    k->S1->Force( -F, R );
    k->S2->Force( F, R );

    k->S1->Torque( -F * (PERP_DOT( R, k->R1 ) - PERP_DOT( R, k->S1->R )) );
    k->S2->Torque( F * (PERP_DOT( R, k->R2 ) - PERP_DOT( R, k->S2->R )) );
    if (Dist > k->FractureT || Dist < k->FractureC)
    {
            #pragma omp critical
            {
                remSequenc.push_back(k);
            }
    }*/
    if (calculaMola(k))
    {
        #pragma omp critical
        {
            remSequenc.push_back(k);
        }
    }

    #if (!defined TESTE_SEMAFORO) && (!defined SEMAFORO)
    k->S1->foiAcessado=false;
    k->S2->foiAcessado=false;
    #endif

    #if ((defined TESTE_SEMAFORO) || (defined SEMAFORO))
    omp_unset_lock(&(k->S1->semaforo));
    omp_unset_lock(&(k->S2->semaforo));
    #endif

    return k->Next;
}
void ListaMolasParalelo::finalizaOperacoes ()
{
    #pragma omp single
    {
        if (!remSequenc.empty())
        {
            //Redistribua a lista entre os processadores
            int tamanho=0;
            int i=0;
            int offset;

            #ifdef TESTE
            assert( modelo->SPRList.Begin != NULL );
            assert( !remSequenc.empty() );
            #endif

            qtdMolas -=remSequenc.size();
            for (list<SPRING*>::iterator it=remSequenc.begin() ; it != remSequenc.end(); it++ )
            {
                modelo->Delete(*it);
            }
            if (qtdMolas < 100) modelo->numProceMola=1;
            offset=qtdMolas/modelo->numProceMola;
            modelo->threadsS[0]=modelo->SPRList.Begin;
            for (SPRING* k = modelo->threadsS[0]; k != NULL; k = k->Next)
            {
                tamanho++;
                if (tamanho == offset)
                {
                    i++;
                    modelo->threadsS[i] = k;
                }
            }
            remSequenc.clear();

            #ifdef TESTE
            assert( remSequenc.empty() );
            int numMolas=0;
            for (SPRING* k = modelo->threadsS[0]; k != NULL; k = k->Next) numMolas++;
            assert(numMolas==qtdMolas);
            #endif
        }

    }
    //barreira implícita
}
